import Food from './Food'
import MoveArea from './MoveArea'

class Snake {
  // 对应的蛇容器的dom元素
  private element: HTMLElement
  private snakeHead: HTMLElement
  private headStyleList = [
    '🐵',
    '🐶',
    '🐺',
    '🦊',
    '🦝',
    '🐱',
    '🦁',
    '🐯',
    '🐴',
    '🦄',
    '🦓',
    '🐮',
    '🐷',
    '🦒',
    '🐭',
    '🐹',
    '🐰',
    '🐻',
    '🐨',
    '🐼',
    '🐔',
    '🐸',
    '🐲'
  ]
  private bodyStyleList = [
    // '📕',
    // '📗',
    // '📘',
    // '📙',
    // '📒',
    // '📔',
    // '🎱',
    // '⚽',
    // '⚾',
    // '🥎',
    // '🏀',
    // '🏐',
    // '🎃',
    '🔴',
    '🟠',
    '🟡',
    '🟢',
    '🔵',
    '🟣',
    '🟤',
    '⚫',
    '⚪'
    // '🟥',
    // '🟧',
    // '🟨',
    // '🟩',
    // '🟦',
    // '🟪',
    // '🟫',
    // '⬜',
    // '♈',
    // '♉',
    // '♊',
    // '♋',
    // '♌',
    // '♍',
    // '♎',
    // '♏',
    // '♐',
    // '♑',
    // '♒',
    // '♓',
    // '⛎'
  ]
  // 每一节身体，包括头
  private itemEleList: HTMLCollection
  constructor() {
    this.element = document.getElementById('snake')!
    this.snakeHead = document.getElementById('snake-head')!
    this.itemEleList = this.element.children
    this.resetStatus()
  }

  /**
   * 相对于移动区域的x/left
   */
  get postionX() {
    return MoveArea.getRelativeCoordinates(this.snakeHead).x
  }

  /**
   * 相对于移动区域的y/top
   */
  get postionY() {
    return MoveArea.getRelativeCoordinates(this.snakeHead).y
  }

  get width() {
    return this.snakeHead.getBoundingClientRect().width
  }

  get height() {
    return this.snakeHead.getBoundingClientRect().height
  }

  get snakeLength() {
    return this.itemEleList.length
  }

  /**
   * 随机改变头的位置
   */
  private changeHeadPosition() {
    //10vw/1vw=10
    //10再减1=9
    let wnum = MoveArea.width / this.width - 1
    let hnum = MoveArea.height / this.height - 1
    //随机0~9
    wnum = Math.random() * wnum
    hnum = Math.random() * hnum
    //9*1vw=9vw
    //left=9vw
    //wnum必须是整数，蛇一节身体的大小和食物大小一样，
    //蛇移动时是移动一节身体的宽度或者高度
    this.snakeHead.style.left = Math.floor(wnum) * this.width + 'px'
    this.snakeHead.style.top = Math.floor(hnum) * this.height + 'px'
    //改变头的样式
    this.changeHeadStyle()
  }

  /**
   * 设置头的样式
   */
  public changeHeadStyle() {
    const index = Math.round(Math.random() * (this.headStyleList.length - 1))
    this.snakeHead.innerText = this.headStyleList[index]
  }

  /**
   * 设置身体的样式
   * @param element
   */
  private setBodyStyle(element: HTMLElement) {
    const index = Math.round(Math.random() * (this.bodyStyleList.length - 1))
    element.innerText = this.bodyStyleList[index]
  }

  /**
   * 向某个方向移动一个身位
   * @param direction
   */
  public move(direction: 'up' | 'down' | 'left' | 'right') {
    if (direction === 'up') {
      this.changeTopOrLeft('top', '-')
    }
    if (direction === 'down') {
      this.changeTopOrLeft('top', '+')
    }
    if (direction === 'left') {
      this.changeTopOrLeft('left', '-')
    }
    if (direction === 'right') {
      this.changeTopOrLeft('left', '+')
    }
  }
  private changeTopOrLeft(flag: 'left' | 'top', method: '+' | '-') {
    //保存上一节身体的位置信息
    let lastPostion: { top: number; left: number } = { top: 0, left: 0 }
    for (let i = 0; i < this.itemEleList.length; i++) {
      const item = this.itemEleList[i] as HTMLElement
      const postion = MoveArea.getRelativeCoordinates(item)
      const top = postion.y
      const left = postion.x
      // 头部
      if (i === 0) {
        let height_width: number = 0
        let top_left: number = 0
        if (flag === 'left') {
          height_width = this.width //获取一节身体的宽度
          top_left = left //获取left的值
        } else if (flag === 'top') {
          height_width = this.height //获取一节身体的高度
          top_left = top //获取top的值
        }
        if (method === '+') {
          item.style[flag] = top_left + height_width + 'px'
        } else if (method === '-') {
          item.style[flag] = top_left - height_width + 'px'
        }
      }
      // 非头部的身体要移动到上一节身体的位置
      else {
        item.style.top = lastPostion.top + 'px'
        item.style.left = lastPostion.left + 'px'
      }

      lastPostion.top = top
      lastPostion.left = left
    }
  }

  /**
   * 添加身体
   * @param moveDirection 当前蛇的移动方向
   */
  public addBody(moveDirection: 'up' | 'down' | 'left' | 'right') {
    //只有头的情况，根据移动方向进行添加
    if (this.itemEleList.length === 1) {
      //往反方向添加
      if (moveDirection === 'up') {
        this.assistAddBody('down')
      }
      if (moveDirection === 'down') {
        this.assistAddBody('up')
      }
      if (moveDirection === 'left') {
        this.assistAddBody('right')
      }
      if (moveDirection === 'right') {
        this.assistAddBody('left')
      }
    }
    // 有身体的情况，根据最后1节和倒数第2节身体的相对位置情况进行添加
    else if (this.itemEleList.length > 1) {
      const last1 = this.itemEleList[this.itemEleList.length - 1] as HTMLElement
      const last2 = this.itemEleList[this.itemEleList.length - 2] as HTMLElement
      //如果两个元素的left值相等，代表他们处于一条垂直线上
      if (last1.offsetLeft === last2.offsetLeft) {
        /**
         * 要添加的元素
         *      |
         * 倒数第1个元素
         *      |
         * 倒数第2个元素
         */
        if (last2.offsetTop - last1.offsetTop > 0) {
          this.assistAddBody('up')
        } else {
          this.assistAddBody('down')
        }
      }
      //如果两个元素的top值相等，代表他们处于一条水平线上
      if (last1.offsetTop === last2.offsetTop) {
        //要添加的元素->倒数第1个元素->倒数第2个元素
        if (last2.offsetLeft - last1.offsetLeft > 0) {
          this.assistAddBody('left')
        } else {
          this.assistAddBody('right')
        }
      }
    }
  }

  /**
   * 添加一节身体
   * @param direction 要添加在当前最后一节身体的什么方向
   */
  private assistAddBody(direction: 'up' | 'down' | 'left' | 'right') {
    const lastEle = this.itemEleList[this.itemEleList.length - 1] as HTMLElement //最后一节身体
    //最后一节身体的信息
    let top = MoveArea.getRelativeCoordinates(lastEle).y
    let left = MoveArea.getRelativeCoordinates(lastEle).x
    let height = this.height
    let width = this.width
    // 要添加的身体
    const newEle = document.createElement('div')
    newEle.classList.add('item')
    this.setBodyStyle(newEle)
    newEle.style.top = top + 'px'
    newEle.style.left = left + 'px'
    if (direction === 'up') {
      newEle.style.top = top - height + 'px'
    }
    if (direction === 'down') {
      newEle.style.top = top + height + 'px'
    }
    if (direction === 'left') {
      newEle.style.left = left - width + 'px'
    }
    if (direction === 'up') {
      newEle.style.left = left + width + 'px'
    }
    this.element.appendChild(newEle)
  }

  /**
   * 是否吃到食物
   */
  public isEatFood() {
    const foodX = Math.floor(Food.postionX)
    const foodY = Math.floor(Food.postionY)
    const snakeX = Math.floor(this.postionX)
    const snakeY = Math.floor(this.postionY)
    // `foodX + i === snakeX + i && foodY + i === snakeY + i `
    // 有 +,-5px的容错值
    for (let i = 1; i <= 5; i++) {
      for (let j = 1; j <= 2; j++) {
        const symbol = j == 1 ? '+' : '-'
        for (let k = 0; k < 16; k++) {
          const m = `${symbol} ${i}`
          let str: string = ''
          switch (k) {
            case 0:
              str = `foodX  === snakeX && foodY === snakeY `
              break
            case 1:
              str = `foodX  === snakeX  && foodY === snakeY ${m} `
              break
            case 2:
              str = `foodX === snakeX && foodY ${m}  === snakeY `
              break
            case 3:
              str = `foodX === snakeX && foodY ${m} === snakeY ${m}  `
              break
            case 4:
              str = `foodX === snakeX ${m} && foodY === snakeY `
              break
            case 5:
              str = `foodX  === snakeX  ${m}  && foodY  === snakeY ${m}`
              break
            case 6:
              str = `foodX  === snakeX ${m} && foodY ${m}  === snakeY  `
              break
            case 7:
              str = `foodX === snakeX ${m} && foodY ${m} === snakeY ${m} `
              break
            case 8:
              str = `foodX ${m} === snakeX && foodY  === snakeY `
              break
            case 9:
              str = `foodX ${m} === snakeX  && foodY === snakeY  ${m}`
              break
            case 10:
              str = `foodX ${m} === snakeX  && foodY ${m} === snakeY `
              break
            case 11:
              str = `foodX ${m} === snakeX  && foodY ${m} === snakeY ${m}`
              break
            case 12:
              str = `foodX ${m} === snakeX ${m}  && foodY === snakeY `
              break
            case 13:
              str = `foodX ${m} === snakeX ${m}  && foodY === snakeY ${m} `
              break
            case 14:
              str = `foodX ${m} === snakeX ${m}  && foodY ${m} === snakeY  `
              break
            case 15:
              str = `foodX ${m} === snakeX ${m}  && foodY ${m} === snakeY ${m} `
              break
          }

          if (eval(str)) {
            return true
          }
        }
      }
    }

    return false
  }

  /**
   * 是否撞墙
   */
  public isHitTheWall() {
    if (
      this.postionX >= MoveArea.width ||
      this.postionY >= MoveArea.height ||
      this.postionX < -1 ||
      this.postionY < -1
    ) {
      return true
    }
    return false
  }

  /**
   * 是否碰撞身体
   */
  public isCollidedTheBody() {
    //如果头部的坐标和身体某一节的坐标相同，则发生了碰撞
    for (let i = 0; i < this.itemEleList.length; i++) {
      const item = this.itemEleList[i] as HTMLElement
      if (item.id !== 'snake-head') {
        if (
          item.offsetLeft == this.snakeHead.offsetLeft &&
          item.offsetTop == this.snakeHead.offsetTop
        ) {
          return true
        }
      }
    }
    return false
  }

  /**
   * 重设状态
   */
  public resetStatus() {
    //删除身体
    const list = [...this.itemEleList]
    //不能直接遍历this.itemEleList
    //因为删除元素时,this.itemEleList的大小也会跟着改变
    for (const item of list) {
      if (item.id !== 'snake-head') {
        item.remove()
      }
    }
    //改变头的位置
    this.changeHeadPosition()
  }
}

export default new Snake()
